package com.spring.core.servlet;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Field;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.spring.core.anotation.Autowired;
import com.spring.core.anotation.Controller;
import com.spring.core.anotation.RequestMapping;
import com.spring.core.anotation.Respotitory;
import com.spring.core.anotation.Service;
import com.spring.core.model.InternalResourceViewResolver;
import com.spring.core.model.SpringMappings;
import com.spring.core.utils.ArrayUtil;
import com.spring.core.utils.BeanUtils;
import com.spring.core.utils.ClassUtils;
import com.spring.core.utils.ReflectUtil;
import com.spring.core.utils.StringUtils;
import com.spring.core.xml.XmlScan;

/**
 * @Author 鐞﹀摜 110
 * @Date 2018/7/5 11:18
 */
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private List<Class<?>> classesList = Collections.synchronizedList(new ArrayList<Class<?>>());
	//IOC容器
    private Map<String, Object> contextContainer = Collections.synchronizedMap(new HashMap<String, Object>());
    //url装载器。
    private Map<String,Object> urlMapping = Collections.synchronizedMap(new HashMap<String,Object>());
    //配置文件
    private String contextConfigLocation;
    
    private SpringMappings springMapping = null;
    
    public void setContextConfigLocation(String contextConfigLocation) {
        this.contextConfigLocation = contextConfigLocation.replace("classpath:", "");
    }
    public void init() throws ServletException {
    	super.init();
    	  setContextConfigLocation(this.getInitParameter("contextConfigLocation"));
    	try {
    		 springMapping = XmlScan.configure(contextConfigLocation);
    		 System.out.println(springMapping.getBasePackage());
    		scanPackageAnnotationClass(springMapping.getBasePackage());
			doIoc();
			doDI();
		 urlMappingMethod();
		} catch (Exception e) {
			e.printStackTrace();
		}

    }
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	InternalResourceViewResolver resolver = new InternalResourceViewResolver(springMapping.getPrefix(), springMapping.getSuffix());
    	String uri =request.getRequestURI();
    	String contextPath = request.getContextPath();
    	String urlMethodMapping = uri.replace(contextPath, "");
    	Method method = (Method)urlMapping.get(urlMethodMapping);    	
     try {
    	 Object url = initMapping(method,request,response);
    		 if(url!=null) {
    			String baseUrl =url.toString();
    			  if (baseUrl.startsWith(resolver.REDIRECT_URL_PREFIX)) {
                    response.sendRedirect(request.getContextPath() +resolver.getJspLocation(StringUtils.trimByPrefix(baseUrl, resolver.REDIRECT_URL_PREFIX)) );
    			}else if(baseUrl.startsWith(resolver.FORWARD_URL_PREFIX)) {
    				request.getRequestDispatcher(resolver.getJspLocation(StringUtils.trimByPrefix(baseUrl,resolver.FORWARD_URL_PREFIX))).forward(request, response);
    			}else {
    				request.getRequestDispatcher(resolver.getJspLocation(baseUrl)).forward(request, response);
    			}
    			 
    		 }else {
    			 System.out.println("");
    		 }	 
     }catch(Exception e) {
    	 e.printStackTrace();
     }
    	
    	
    }
    private Object initMapping(Method method,HttpServletRequest request,HttpServletResponse response){
    	List<Object> parameterList =null;
    	Object url =null;
    	 try {
    	if(method !=null) {
   		 Class<?>[] parameterTypes = method.getParameterTypes();
   		 if(ArrayUtil.isNotEmpty(parameterTypes)) {
   			 parameterList =  new ArrayList<Object>();
   			 for(Class<?> cl:parameterTypes) {
   				if(!cl.isInterface()) {
   					if(ClassUtils.isSystemClass(cl)) {
   						parameterList.add(BeanUtils.convertHttpServletRequestToSystemClassBean(request, cl));
   					}else {
   						parameterList.add(BeanUtils.convertHttpServletRequestToJavaBean(request, cl));
   					}
   				}else {
   					if(cl == HttpServletRequest.class) {
   						parameterList.add(request);
   					}else if(cl== HttpServletResponse.class) {
   						parameterList.add(response);
   					}else if(cl == HttpSession.class) {
   						parameterList.add(request.getSession());
   					}else if(cl == ServletContext.class) {
   						parameterList.add(this.getServletContext());
   					}
   				}
   				 
   			 } 
   		 }
	   		 Class<?> declaringClass = method.getDeclaringClass();
	   		 String alias = ReflectUtil.getAnnotationAlias(declaringClass);
	   		 Object obj = contextContainer.get(alias);
	   		 if(null!=parameterList && parameterList.size()>0) {
	   			 url = method.invoke(obj, parameterList.toArray());
	   		 }else {
	   			 url = method.invoke(obj);
			}
   			}
    	}catch (Exception e) {
		e.printStackTrace();
    	}
	  
    	return  url;
    	}
    	
    private void doIoc() throws Exception {
        if (classesList.size()==0){
            return;
        }
        for(int i = 0; i < classesList.size();i++) {
        	Class<?> clazz = classesList.get(i);
				String alias = ReflectUtil.getAnnotationAlias(clazz);
				contextContainer.put(alias, clazz.newInstance());
        }
        
    }
  
    private void urlMappingMethod() {
    	if(classesList.size()==0) {
    		return;
    	}
    	for(int i =0;i <classesList.size();i++) {
    		Class<?> clazz = classesList.get(i);
    		if(clazz.isAnnotationPresent(Controller.class)) {
    			String baseUrl =null;
    			if(clazz.isAnnotationPresent(RequestMapping.class)) {
    		RequestMapping requestMapping= clazz.getAnnotation(RequestMapping.class);
    			if(!"".equals(requestMapping.value())) {
    				baseUrl= ReflectUtil.handlerUrl(requestMapping.value());
    			}
    			Method[] methods = clazz.getDeclaredMethods();
    			if(ArrayUtil.isNotEmpty(methods)) {
    				for(Method method:methods) {
    					 if (method.getModifiers() == Modifier.PUBLIC &&
                              method.isAnnotationPresent(RequestMapping.class)) { {
                         String methodUrl=null;
    					RequestMapping requestM = method.getAnnotation(RequestMapping.class);
    					 if(!"".equals(requestM.value())){
    						String url = ReflectUtil.handlerUrl(requestM.value());
    						if(baseUrl !=null) {
    							methodUrl = baseUrl+url;
    						}else {
    							methodUrl =url;
    						}
    						urlMapping.put(methodUrl, method);
    					 }
    					 
    					}
    				}
    				
    			}
    		}
    		}
    		
    
    		
    	}}
    	
    	
    }
    
  private void doDI() throws Exception{
	  if(classesList.size()==0) {
		  return ;
		  
	  }
	  for(int i = 0; i < classesList.size();i++) {
		  Class<?> clazz = classesList.get(i);
		  String alias = ReflectUtil.getAnnotationAlias(clazz);
		  Object annotationInstance = contextContainer.get(alias);
		     Field[] fields = clazz.getDeclaredFields();
		     if(ArrayUtil.isNotEmpty(fields))
		     for(Field field:fields) {
		    	 Object inject =null;
		    	 if(field.isAnnotationPresent(Autowired.class)) {
		    	Autowired fieldName = field.getAnnotation(Autowired.class);
		    	if(!"".equals(fieldName.value())) {
		    		String alisa = fieldName.value();
		    		inject= contextContainer.get(alisa);
		    		
		    	}else {
		    		Class<?> claz = field.getType();
		    		Collection<Object> map = contextContainer.values();
		    		Iterator<Object> it = map.iterator();
		    		while (it.hasNext()) {
		    				Object obj = it.next();
		    				if(claz.isAssignableFrom(obj.getClass())) {
		    					inject =obj;
		    					break;
		    				}
						
					}
		    	}
		    	 }
		    	 field.setAccessible(true);
		    	 field.set(annotationInstance, inject);
		    	 field.setAccessible(false); 
		     }
		     }
	  }
	  
    private void scanPackageAnnotationClass(final String path){
        URL url = this.getClass().getClassLoader().getResource(path.replaceAll("\\.", "/"));
        try {
        File file = new File(url.toURI());
        file.listFiles(new FileFilter() {
            public boolean accept(File pathname) {
                if(pathname.isDirectory()){
                    scanPackageAnnotationClass(path+"."+pathname.getName());
                }else{
                        try {
                        	 if (pathname.getName().endsWith(".class")){
                                 String className = pathname.getName().replaceAll("\\.class","");
                            Class<?> clazz = this.getClass().getClassLoader().loadClass((path + "." + className));
                            if (clazz.isAnnotationPresent(Controller.class)||clazz.isAnnotationPresent(Service.class)||clazz.isAnnotationPresent(Respotitory.class)){
                                classesList.add(clazz);
                            }
                        } }catch (ClassNotFoundException e) {
                          e.printStackTrace();
                        }
                   
                }
                return false;
            }
        });
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    public Object getContextBean(String beanName) {
        return contextContainer.get(beanName);
    }





}
